/*
 * =====================================================================================
 *
 *       Filename:  fifo.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  06/30/15 18:31:01
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Andy (gk), andy_y_li@163.com
 *        Company:  RD
 *
 * =====================================================================================
 */

#include "fifo.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>

void fifo_init(FIFO *fifo, int size, char *buf)
{
    fifo->buf = buf;
    fifo->size = size;
    fifo->putP = 0;
    fifo->getP = 0;
    fifo->dumpBuf = NULL;
}

bool fifo_put(FIFO *fifo, unsigned char data)
{
    if (fifo->putP + 1 == fifo->size) {
        fifo->getP = fifo->putP = 0;
    }
    fifo->buf[fifo->putP] = data;
    fifo->putP++;
    fifo->putP %= fifo->size; 

    if (fifo->putP < fifo->getP) {
        fifo->getP = 0;
    }
    return true;
}

int fifo_get(FIFO *fifo)
{
    int index;
    if (fifo->getP >= fifo->putP) {
        return -1;
    }
    index = fifo->getP;
    fifo->getP++;
    fifo->getP %= fifo->size;

    return index;
}

int fifo_status(FIFO *fifo)
{
    return (fifo->putP - fifo->getP);
}

int fifo_free(FIFO *fifo)
{
    return (fifo->size - fifo->putP);
}

bool fifo_add(FIFO *fifo, const char*buf, int length)
{
    int free_len = fifo_free(fifo);
    length = (strlen(buf) > length? length:strlen(buf));
    if (free_len >= length) {
        if (free_len == length) {
            // And then it will take an enough space 
            // for store the latest data. 
            // although it will throw away (putP - getP)'s data
            fifo->putP = (fifo->putP > fifo->getP?
                    fifo->getP:fifo->putP);
        }
        strncpy(&(fifo->buf[fifo->putP]), buf, length);
        fifo->putP += length;
        fifo->putP %= fifo->size;
    }
    else {
        if (length > fifo->size) {
            // Just store the rest l's data
            int l = length % fifo->size;
            strncpy(fifo->buf, buf + length - l, l);
            fifo->getP = 0;
            fifo->putP = l;
        }
        else {
            if (length >= (fifo->size - fifo->getP)) {
                fifo->getP = fifo->putP = fifo->size - length - 1;
            }
            else {
                fifo->putP = fifo->size - length - 1;
            }
            strncpy(&(fifo->buf[fifo->putP]), buf, length);
            fifo->putP += length;
            fifo->putP %= fifo->size;

        }
    }

    if (fifo->putP < fifo->getP) {
        fifo->getP = 0;
    }
    return true;
}

char *fifo_dump(FIFO *fifo)
{
    int length = fifo_status(fifo);
    if (length == 0) {
        return NULL;
    }
    // Free the buffer has malloc last time.
    if (fifo->dumpBuf) {
        free(fifo->dumpBuf);
        fifo->dumpBuf = NULL;
    }
    fifo->dumpBuf = (char *)malloc((length + 1) * sizeof(char));
    if (fifo->dumpBuf == NULL) {
        printf("Error on malloc: %s\n", strerror(errno));
        return NULL;
    }
    memset(fifo->dumpBuf, '\0', (length + 1) * sizeof(char));
    strncpy(fifo->dumpBuf, fifo->buf + fifo->getP, length);
    fifo->getP = fifo->putP;
    return fifo->dumpBuf;
}

